/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  Copyright (C) 2020. Huawei Technologies Co., Ltd. All rights reserved.
 *  Provide angel core method to monitor kernel start.
 */

#ifndef LINUX_ANGEL_CORE_H
#define LINUX_ANGEL_CORE_H

#ifdef CONFIG_EULEROS_ANGEL_CORE_MONITOR
#include <linux/kexec.h>

#define ANGEL_COMM_ROLLBACK 0x1234
#define ANGEL_WAKEUP 1
#define ANGEL_TOUCHED 2
#define ANGEL_UNTOUCHED 0
#define ANGEL_RETIRE 4
#define ANGEL_MEM_MIN_SIZE  (60 * PAGE_SIZE)
#define MIN_ANGEL_MONITOR_TIMEOUT_MS 500
#define DEFAULT_ANGEL_MONITOR_TIMEOUT_MS 1000
#ifndef CONFIG_KASAN
#define MAX_ANGEL_MONITOR_TIMEOUT_MS 10000
#else
#define MAX_ANGEL_MONITOR_TIMEOUT_MS 50000
#endif
#define ANGEL_CORE_STACK_PAGE_OFFSET  3
#define ANGEL_CORE_POOL_START_PAGE_OFFSET 4
#define ANGEL_CORE_POOL_END_PAGE_OFFSET 48

enum angel_state {
	STATE_STANDBY,
	STATE_OBSERVING,
	STATE_HALT,
};

struct angel_page_pool {
	unsigned long start;
	unsigned long end;
	unsigned long (*alloc_one_page)(struct angel_page_pool *pool);
};

/* |   data   |  code  |  stack  |  stack  | pgtables |....  */
/* |    4K    |   4K   |   4K    |   4K    |          |      */
struct angel_core_section {
	unsigned long phys;		/* physical address of this section */
	unsigned long pgd;		/* pgd maybe used */
	unsigned long flag;		/* flag maybe used */
	unsigned long stack;
	unsigned long cpu;		/* this cpu */
	unsigned long start;
	unsigned long end;
	unsigned long comm;
	unsigned long exit;	/* reserved */
	unsigned long apicid;
	unsigned long retire;
	unsigned long boot_core;
	unsigned long x2apic;
	unsigned long timeout;
	unsigned int event;
	char text[0];			/* text section of park */
};

extern bool cpu_park_enable;
extern unsigned int __read_mostly tsc_khz;
extern unsigned long mp_lapic_addr;
extern unsigned long angel_section_phys;
extern unsigned int get_angel_core(void);
extern int wake_angel_core(unsigned int cpu);
extern unsigned long angel_loop(unsigned long pgd_addr,
			unsigned long park_code_addr,
			unsigned long exit_addr);
extern unsigned long real_angel_loop(unsigned long pgd_addr,
			unsigned long park_code_addr,
			unsigned long exit_addr);
extern int angel_start_monitor(void);
extern void __init set_angel_mem_area(unsigned long start, unsigned long len);
extern void __init init_angel_monitor_core(unsigned long start);
extern void wait_for_angel_core(unsigned int timeout);
extern struct kup_monitor_operations *get_angel_core_monitor_ops(void);
extern bool is_angel_core_section_valid(void);
static inline struct angel_core_section *angel_core_section_virt(void)
{
	return (struct angel_core_section *)phys_to_virt(angel_section_phys);
}
static inline struct angel_core_section *angel_core_section_phys(void)
{
	return (struct angel_core_section *)angel_section_phys;
}
static inline unsigned long angel_core_text_addr(struct angel_core_section *section)
{
	return PAGE_ALIGN((unsigned long)&section->text);
}
static inline unsigned long *angel_core_exit_addr(struct angel_core_section *section,
						unsigned int cpu)
{
	return &section->exit;
}
static inline void touch_angel(void)
{
	struct angel_core_section *section;

	if (angel_core_section_phys()) {
		section = angel_core_section_virt();
		if (section)
			section->flag = ANGEL_TOUCHED;
	}
}

static inline void set_angel_section_core(unsigned int cpu)
{
	struct angel_core_section *section;

	if (angel_core_section_phys()) {
		section = angel_core_section_virt();
		if (section)
			section->cpu = cpu;
	}
}

static inline void retire_angel(void)
{
	struct angel_core_section *section;

	if (angel_core_section_phys()) {
		section = angel_core_section_virt();
		if (section)
			section->retire = 1;
	}
}
#endif
#endif /* LINUX_KEXEC_H */
